#include "audiocapturer.h"
#include"dlog.h"
#include"timesutil.h"
#include"avpublishtime.h"
extern "C" {
#include <libavcodec/avcodec.h>
}

AudioCapturer::AudioCapturer()
{

}

AudioCapturer::~AudioCapturer()
{
    if(_pcm_buf)
    {
        delete[] _pcm_buf;
    }
    if(_pcm_fp)
    {
        fclose(_pcm_fp);
    }
}

RET_CODE AudioCapturer::init(const Properties properties)
{
    // todo 底层框架的参数不能够硬编码，需要修复，fix me
    /// AudioCapturer -》 PushWork   AudioCapturer就是最底层的，那么AudioCapturer 尽量不要硬编码，在 PushWork允许部分硬编码，毕竟PushWork是最高层，方便修改，且不会造成其他影响。
    ///  AudioCapturer 是提供给许多上层使用的，一旦AudioCapturer修改了，那么可能对许多其他地方造成影响。
    ///
    _audio_test = properties.GetProperty("audio_test", 0);
    _input_pcm_name = properties.GetProperty("input_pcm_name", "D:\\data\\video\\demo\\48000_2_s16le.pcm");
    _sample_rate = properties.GetProperty("sample_rate", 48000);
    _channels = properties.GetProperty("channels", 2);
    _nb_samples = properties.GetProperty("nb_samples", 1024);
    _format = properties.GetProperty("format", AV_SAMPLE_FMT_S16);
    _byte_per_samples = properties.GetProperty("byte_per_samples", 2);//fix me,这里应该是由_format推算得出

    _pcm_buf_size = _byte_per_samples * _channels * _nb_samples; //标准每一帧的pcm的需要的字节大小
    _pcm_buf = new uint8_t[_pcm_buf_size];// 创建pcm_buf的内存空间
    // 分配内存失败
    if(!_pcm_buf)
    {
        LogError("allolc _pcm_buf failed");
        return RET_ERR_OUTOFMEMORY;
    }
    // 打开文件失败
    if(openPcmFile(_input_pcm_name.c_str()) < 0)
    {
        LogError("openPcmFile %s failed", _input_pcm_name.c_str());
    }
    _frame_duration = 1.0 * _nb_samples / _sample_rate * 1000;// 每一帧的间隔时长【或者说每一帧的播放时长】
    return RET_OK;
}

void AudioCapturer::loop()
{
    LogInfo("into");
    _pcm_total_duration = 0;//从现在开始计算推流的时长
    _pcm_start_time = TimesUtil::GetTimeMillisecond();//从现在开始记录推流的开始时间
    // 重复读取pcm数据
    while(true)
    {
        // 请求退出线程
        if(_request_abort)
        {
            break;
        }
        if(readPcmFile(_pcm_buf, _pcm_buf_size) == 0)
        {
            if(!_is_first_time)
            {
                _is_first_time = true;
                LogInfo("%s: t%u",AVPublishTime::GetInstance()->getAInTag(),
                        AVPublishTime::GetInstance()->getCurrenTime());
            }
            // 如果有回调函数，那么将获取到的pcm数据传给回调函数
            if(_callback)
            {
                _callback(_pcm_buf, _pcm_buf_size);
            }
        }
        // 睡眠2毫秒防止读取太快了，让cpu可以切换
        std::this_thread::sleep_for(std::chrono::milliseconds(2));
    }
    // 当线程执行完毕后，恢复 请求停止标识 状态。
    _request_abort = false;
    closePcmFile();
}

void AudioCapturer::addCallback(function<void (uint8_t *, int32_t)> callback)
{
    _callback = callback;
}

int AudioCapturer::openPcmFile(const char *fileName)
{
    _pcm_fp = fopen(fileName, "rb");
    if(!_pcm_fp)
    {
        return -1;
    }
    return 0;
}

int AudioCapturer::readPcmFile(uint8_t *pcmBuf, int32_t pcmBufSize)
{
    // 记录从推流开始到现在过了多长时间
    int64_t dif = TimesUtil::GetTimeMillisecond() - _pcm_start_time;
    // 如果上一帧的数据还没发送【播放】完毕，是不需要读取新的一帧数据
    if(((int64_t)_pcm_total_duration) > dif)
    {
        return 1;
    }
    // 从pcm文件中读取数据
    size_t ret = fread(pcmBuf, 1, pcmBufSize, _pcm_fp);
    // 如果文件中剩余的数据不够_pcm_buf_size这么多了
    if(ret != (size_t)pcmBufSize)
    {
        // 跳转到文件起始的位置开始重新读取数据,[即重复从该文件读取数据，不停止]
        ret = fseek(_pcm_fp, 0, SEEK_SET);
        size_t ret = fread(pcmBuf, 1, pcmBufSize, _pcm_fp);
        // 如果文件中剩余的数据仍然不够 _pcm_buf_size，代表该视频文件连一帧的数据都没有（或者该文件损坏了）。
        if(ret != (size_t)pcmBufSize)
        {
            return -1; //返回读取失败
        }
    }
    // 更新当前推流的总时长，即现在的数据可以支持播放多久。【也代表下一帧的数据在多久后才需要被读取出来】
    _pcm_total_duration += _frame_duration;
    return 0;
}

int AudioCapturer::closePcmFile()
{
    if(_pcm_fp)
    {
        fclose(_pcm_fp);
    }
    return 0;
}
